
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Xml;

using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace Derm.BindGen
{
	/// <summary>
	/// Binding generator.
	/// </summary>
	class BindGenerator
	{
		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		/// <seealso cref=""/>
		public static void Main (string[] args)
		{
			//args = new string[] { "PatchDoxygenHtml", "../../../Derm/Doc/htmluser" };
			//args = new string[] { "GenColladaClasses", "../../../Derm/Xml/Schemas", "../../../Derm/Xml" };

			if (args.Length > 0) {
				if (args[0] == "GenBindings") {
					BuildBindings();
				} else if (args[0] == "PatchDoxygenHtml") {
					if (args.Length > 1) {
						PatchDoxygenHtml(args[1]);
					} else
						Console.WriteLine("Missing Doxygen HTML documentation path");
				} else if (args[0] == "GenColladaClasses") {
					
				} else {
					Console.WriteLine("Unknown action {0}", args[0]);
				}
			} else
				BuildBindings();
		}

		#region BindGenerator Tasks

		private static void BuildBindings()
		{
			const bool EXCLUDE_DEPRECATED = false;

			SpecDocFactory sDocs;				// Common to all specifications

			SpecDictionary sGlDictionary, sWglDictionary, sGlxDictionary;
			SpecParser sGlParser, sWglParser, sGlxParser;
			EnumClassFactory sGlEnums, sWglEnums, sGlxEnums;
			FunctionClassFactory sGlFuncts, sWglFuncts, sGlxFuncts;

			bool buildGLBindings = false, buildWGLBindings = false, buildGLXBindings = false;
			bool buildDocumentationGL2, buildDocumentationGL4 = false;

			#region User Iteraction

			Console.Write("Do you want to generated GL bindings? [yn] ");
			buildGLBindings = Console.ReadLine() == "y";
			Console.Write("Do you want to generated WGL bindings? [yn] ");
			buildWGLBindings = Console.ReadLine() == "y";
			Console.Write("Do you want to generated GLX bindings? [yn] ");
			buildGLXBindings = Console.ReadLine() == "y";
			Console.Write("Do you want to generate bindings documentation (GL4)? [yn] ");
			buildDocumentationGL4 = Console.ReadLine() == "y";
			Console.Write("Do you want to generate bindings documentation (GL2)? [yn] ");
			buildDocumentationGL2 = Console.ReadLine() == "y";

			#endregion

			#region OpenGL Specification

			// Define dictionary
			sGlDictionary = new SpecDictionary();
			sGlDictionary.ParseExtensionList(PATH_SPEC_EXT_LIST);
			sGlDictionary.ParseSuffixesList(PATH_SPEC_SUFFIX_LIST);

			// Parse OpenGL specifications
			if (File.Exists(PATH_SPEC_GL_WORD_LIST) == true)
				sGlDictionary.ParseWordList(PATH_SPEC_GL_WORD_LIST);
			sGlParser = new SpecParser(sGlDictionary);
			sGlParser.EnumPrefix = "GL_";
			sGlParser.FunctPrefix = "gl";
			sGlParser.Namespace = "Gl";
			sGlParser.ParseSpec(PATH_SPEC_GL, null, PATH_SPEC_GL_ENUM, PATH_SPEC_GL_ENUM_EXT, PATH_SPEC_GL_TM, EXCLUDE_DEPRECATED);

			// Update OpenGL dictionary
			sGlDictionary.ParseSpec(sGlParser, PATH_SPEC_GL_WORD_LIST);
			// Update OpenGL function list
			sGlDictionary.ParseFunctionNames(sGlParser, PATH_SPEC_GL_FUNCT_LIST);

			#endregion

			#region Windows OpenGL Specification

			// Define dictionary
			sWglDictionary = new SpecDictionary();
			sWglDictionary.ParseExtensionList(PATH_SPEC_EXT_LIST);
			sWglDictionary.ParseSuffixesList(PATH_SPEC_SUFFIX_LIST);

			// Parse Windows OpenGL specifications
			if (File.Exists(PATH_SPEC_WGL_WORD_LIST) == true)
				sWglDictionary.ParseWordList(PATH_SPEC_WGL_WORD_LIST);
			sWglParser = new SpecParser(sWglDictionary);
			sWglParser.EnumPrefix = "WGL_";
			sWglParser.FunctPrefix = "wgl";
			sWglParser.Namespace = "Wgl";
			sWglParser.ParseSpec(PATH_SPEC_WGL, PATH_SPEC_WGL_EXT, PATH_SPEC_WGL_ENUM, PATH_SPEC_WGL_ENUM_EXT, PATH_SPEC_WGL_TM, EXCLUDE_DEPRECATED);

			// Update Window OpenGL dictionary
			sWglDictionary.ParseSpec(sWglParser, PATH_SPEC_WGL_WORD_LIST);
			// Update Window OpenGL dictionary
			sWglDictionary.ParseFunctionNames(sWglParser, PATH_SPEC_WGL_FUNCT_LIST);

			#endregion
			
			#region X11 OpenGL Specification

			// Define dictionary
			sGlxDictionary = new SpecDictionary();
			sGlxDictionary.ParseExtensionList(PATH_SPEC_EXT_LIST);
			sGlxDictionary.ParseSuffixesList(PATH_SPEC_SUFFIX_LIST);

			// Parse Windows OpenGL specifications
			if (File.Exists(PATH_SPEC_XGL_WORD_LIST) == true)
				sGlxDictionary.ParseWordList(PATH_SPEC_XGL_WORD_LIST);
			sGlxParser = new SpecParser(sGlxDictionary);
			sGlxParser.EnumPrefix = "GLX_";
			sGlxParser.FunctPrefix = "glX";
			sGlxParser.Namespace = "Glx";
			sGlxParser.ParseSpec(PATH_SPEC_XGL, PATH_SPEC_XGL_EXT, PATH_SPEC_XGL_ENUM, PATH_SPEC_XGL_ENUM_EXT, PATH_SPEC_XGL_TM, EXCLUDE_DEPRECATED);

			// Update Window OpenGL dictionary
			sGlxDictionary.ParseSpec(sGlxParser, PATH_SPEC_XGL_WORD_LIST);
			// Update Window OpenGL dictionary
			sGlxDictionary.ParseFunctionNames(sGlxParser, PATH_SPEC_XGL_FUNCT_LIST);

			#endregion

			#region Documentation

			if (buildDocumentationGL2 == true || buildDocumentationGL4 == true) {
				sDocs = new SpecDocFactory();
				sDocs.WriteDocumentation("../../../Derm/OpenGL/Gl.dox", sGlParser);

				if (buildDocumentationGL4 == true)
					ManualDatabase.LoadManuals(PATH_MAN_GL4, sGlParser);
				if (buildDocumentationGL2 == true)
					ManualDatabase.LoadManuals(PATH_MAN_GL2, sGlParser);
			}

			#endregion

			#region Update Bindings

			if (buildGLBindings == true) {
				// Construct OpenGL classes
				sGlEnums = new EnumClassFactory("Derm.OpenGL", "Gl", sGlParser.Enums);
				sGlEnums.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.All);
				sGlFuncts = new FunctionClassFactory("Derm.OpenGL", "Gl", sGlParser.Functs);
				sGlFuncts.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.All);
			}

			if (buildWGLBindings == true) {
				// Construct WGL classes
				sWglEnums = new EnumClassFactory("Derm.OpenGL", "Wgl", sWglParser.Enums);
				sWglEnums.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.None);
				sWglFuncts = new FunctionClassFactory("Derm.OpenGL", "Wgl", sWglParser.Functs);
				sWglFuncts.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.ARB | PartialClassFactory.SourceExt.EXT | PartialClassFactory.SourceExt.OTHERS);
			}

			if (buildGLXBindings == true) {
				// Construct WGL classes
				sGlxEnums = new EnumClassFactory("Derm.OpenGL", "Glx", sGlxParser.Enums);
				sGlxEnums.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.None);
				sGlxFuncts = new FunctionClassFactory("Derm.OpenGL", "Glx", sGlxParser.Functs);
				sGlxFuncts.WriteAssembly("../../../Derm/OpenGL/", PartialClassFactory.SourceExt.ARB | PartialClassFactory.SourceExt.EXT | PartialClassFactory.SourceExt.OTHERS);
			}

			
			#endregion

			Console.ReadLine();
		}

		private static void PatchDoxygenHtml(string path)
		{
			string[] htmlFiles = Directory.GetFiles(path, "*.html");

			foreach (string htmlPath in htmlFiles) {
				FileInfo info = new FileInfo(htmlPath);

				if (info.Length > 1500 * 1024)
					DoxygenManualPatch.SeparateClassMemberPages(htmlPath);
			}

			foreach (string htmlPath in htmlFiles) {
				DoxygenManualPatch.PatchMathJaxScripts(htmlPath);
			}
		}

		#endregion

		#region Manual Paths

		private static readonly string PATH_MAN_GL2 = "../../Specs/Man/OpenGL2";
		private static readonly string PATH_MAN_GL4 = "../../Specs/Man/OpenGL4";

		#endregion

		#region OpenGL Specification Paths

		private static readonly string PATH_SPEC_GL = "../../Specs/Specs/gl.spec";
		private static readonly string PATH_SPEC_GL_ENUM = "../../Specs/Specs/enum.spec";
		private static readonly string PATH_SPEC_GL_ENUM_EXT = "../../Specs/Specs/enumext.spec";
		private static readonly string PATH_SPEC_GL_TM = "../../Specs/Specs/gl.tm";

		#endregion

		#region Windows GL Specification Paths

		private static readonly string PATH_SPEC_WGL = "../../Specs/Specs/wgl.spec";
		private static readonly string PATH_SPEC_WGL_EXT = "../../Specs/Specs/wglext.spec";
		private static readonly string PATH_SPEC_WGL_ENUM = "../../Specs/Specs/wglenum.spec";
		private static readonly string PATH_SPEC_WGL_ENUM_EXT = "../../Specs/Specs/wglenumext.spec";
		private static readonly string PATH_SPEC_WGL_TM = "../../Specs/Specs/wgl.tm";

		#endregion
		
		#region X11 GL Specification Paths
		
		private static readonly string PATH_SPEC_XGL = "../../Specs/Specs/glx.spec";
		private static readonly string PATH_SPEC_XGL_EXT = "../../Specs/Specs/glxext.spec";
		private static readonly string PATH_SPEC_XGL_ENUM = "../../Specs/Specs/glxenum.spec";
		private static readonly string PATH_SPEC_XGL_ENUM_EXT = "../../Specs/Specs/glxenumext.spec";
		private static readonly string PATH_SPEC_XGL_TM = "../../Specs/Specs/glx.tm";
		
		#endregion

		#region User Lists

		private static readonly string PATH_SPEC_EXT_LIST = "../../Specs/Extensions.spec";
		private static readonly string PATH_SPEC_SUFFIX_LIST = "../../Specs/Suffixes.spec";

		private static readonly string PATH_SPEC_GL_FUNCT_LIST = "../../Specs/GlFuncts.spec";
		private static readonly string PATH_SPEC_WGL_FUNCT_LIST = "../../Specs/WglFuncts.spec";
		private static readonly string PATH_SPEC_XGL_FUNCT_LIST = "../../Specs/GlxFuncts.spec";

		private static readonly string PATH_SPEC_GL_WORD_LIST = "../../Specs/GlWords.spec";
		private static readonly string PATH_SPEC_WGL_WORD_LIST = "../../Specs/WglWords.spec";
		private static readonly string PATH_SPEC_XGL_WORD_LIST = "../../Specs/XglWords.spec";

		#endregion
	}
}
